
// 实例化硬件串口Serial1，指定使用串口1
HardwareSerial Acs(1);
// 定义Serial1的波特率为9600
const int serial1BaudRate = 9600;
// 定义Serial1的发送引脚为10
const int serial1RxPin = 16;
// 定义Serial1的接收引脚为11
const int serial1TxPin = 17;

long DataPreTick = 1000;

bool acsStatus = false;

// 存储接收到的数据
std::vector<uint8_t> acsBuffer;

// 定义读取并网侧数据的指令，这是一个字节数组，按照与传感器约定的协议格式组成，用于向传感器请求并网侧数据
byte readGridCommand[] = {0x02, 0x03, 0x00, 0x48, 0x00, 0x08, 0xC4, 0x29};
// 定义读取用电侧数据的指令，同样是一个字节数组，按照协议格式组成，用于向传感器请求用电侧数据
byte readUsageCommand[] = {0x01, 0x03, 0x00, 0x48, 0x00, 0x08, 0xC4, 0x1A};

// ESP8266 CRC16计算函数，用于计算给定字节数组的CRC16校验码，以验证数据的完整性
bool CRC16(const std::vector<uint8_t> &data)
{
    // 提取接收到的数据中最后两个字节作为接收到的CRC16校验码
    uint16_t receivedCRC = (data[36] << 8) | data[35];

    // 初始化CRC值为0xFFFF，这是CRC16计算的初始状态
    uint16_t crc = 0xFFFF;
    // 定义CRC16计算所使用的多项式为0xA001，这是CRC16算法的关键参数
    const uint16_t polynomial = 0xA001;
    for (size_t i = 0; i < data.size() - 2; i++)
    {
        // 将当前字节与CRC值进行异或操作，开始计算CRC
        crc ^= data[i];
        for (int i = 0; i < 8; i++)
        {
            // 判断CRC的最低位是否为1，如果为1则进行特定的移位和异或操作
            if (crc & 0x0001)
            {
                crc = (crc >> 1) ^ polynomial;
            }
            else
            {
                // 如果最低位为0，则仅进行移位操作
                crc >>= 1;
            }
        }
    }
    if (crc != receivedCRC)
    {
        // 计算提取数据的校验和
        sendtoData("acsk校验失败!");
        String dataString = "原数据 : ";
        for (uint8_t dataByte : acsBuffer)
        {
            dataString += String(dataByte, HEX) + " ";
        }
        sendtoData(dataString);
        delay(2000);
        dataString = "校验数据 : ";
        for (uint8_t dataByte : data)
        {
            dataString += String(dataByte, HEX) + " ";
        }
        sendtoData(dataString);
    }

    // 返回计算得到的CRC16校验码
    return crc == receivedCRC;
}

// 查找数据头函数，返回找到的数据头索引，没找到返回std::string::npos
size_t acsFindHeader(const std::vector<uint8_t> &buffer, uint8_t syncHeader0, uint8_t syncHeader1, uint8_t syncHeader2, uint8_t syncHeader3, size_t startIndex = 0)
{
    for (size_t i = startIndex; i < buffer.size(); ++i)
    {
        if ((acsBuffer[i] == syncHeader0 || acsBuffer[i] == syncHeader1) && acsBuffer[i + 1] == syncHeader2 && acsBuffer[i + 2] == syncHeader3)
        {
            return i;
        }
    }
    return std::string::npos;
}

// 将十六进制字节数组转换为十进制数值的函数，用于解析从传感器接收到的数据
uint32_t hexToDec(const uint8_t *hexData, int numBytes)
{
    // 初始化结果变量为0
    uint32_t result = 0;
    for (int i = 0; i < numBytes; i++)
    {
        // 将结果左移8位，为存储下一个字节的数据腾出位置
        result <<= 8;
        // 将当前字节的数据与结果进行或操作，将当前字节添加到结果中
        result |= hexData[i];
    }
    // 返回转换后的十进制数值
    return result;
}

void SensorData(const std::vector<uint8_t> &data)
{
    acsStatus = true;
    // 如果计算得到的CRC16校验码与接收到的CRC16校验码相等，说明数据完整，进行数据解析
    switch (data[0])
    {
    case 0x02:
        // 如果指令的第一个字节为0x01，说明是读取并网侧数据的指令，进行并网侧数据解析
        // 将接收到的数据转换为实际的电压值并赋值给gridVoltage变量
        gridVoltage = (float)hexToDec(&data[3], 4) * 0.0001;
        // 将接收到的数据转换为实际的电流值并赋值给gridCurrent变量
        gridCurrent = (float)hexToDec(&data[7], 4) * 0.0001 < 0.1 ? 0 : (float)hexToDec(&data[7], 4) * 0.0001;
        // 将接收到的数据转换为实际的功率值并赋值给gridPower变量
        gridPower = (float)hexToDec(&data[11], 4) * 0.0001 < 1 ? 0 : (float)hexToDec(&data[11], 4) * 0.0001;
        // 将接收到的数据转换为实际的电能值并赋值给gridEnergy变量
        gridEnergy = (float)hexToDec(&data[15], 4) * 0.0001;
        // 将接收到的数据转换为实际的功率因素值并赋值给gridPowerFactor变量
        gridPowerFactor = (float)hexToDec(&data[19], 4) * 0.001;
        // 将接收到的数据转换为实际的频率值并赋值给gridFrequency变量
        gridFrequency = (float)hexToDec(&data[31], 4) * 0.01;
        break;
    case 0x01:
        // 如果指令的第一个字节为0x02，说明是读取用电侧数据的指令，进行用电侧数据解析
        // 将接收到的数据转换为实际的电压值并赋值给usageVoltage变量
        usageVoltage = (float)hexToDec(&data[3], 4) * 0.0001;
        // 将接收到的数据转换为实际的电流值并赋值给usageCurrent变量
        usageCurrent = (float)hexToDec(&data[7], 4) * 0.0001 < 0.1 ? 0 : (float)hexToDec(&data[7], 4) * 0.0001;
        // 将接收到的数据转换为实际的功率值并赋值给usagePower变量
        usagePower = (float)hexToDec(&data[11], 4) * 0.0001 < 1 ? 0 : (float)hexToDec(&data[11], 4) * 0.0001;
        // 将接收到的数据转换为实际的电能值并赋值给usageEnergy变量
        usageEnergy = (float)hexToDec(&data[15], 4) * 0.0001;
        // 将接收到的数据转换为实际的功率因素值并赋值给usagePowerFactor变量
        usagePowerFactor = (float)hexToDec(&data[19], 4) * 0.001;
        // 将接收到的数据转换为实际的频率值并赋值给usageFrequency变量
        usageFrequency = (float)hexToDec(&data[21], 4) * 0.01;
        break;
        // 可根据实际传感器数量和指令类型继续添加case分支
    }
}

// 修改后的读取数据函数，用于与传感器进行通信，读取数据并解析赋值给相应变量
void readData(const byte *command, size_t commandLength)
{
    const uint8_t syncHeader0 = 0x01;
    const uint8_t syncHeader1 = 0x02;
    const uint8_t syncHeader2 = 0x03;
    const uint8_t syncHeader3 = 0x20;

    size_t prevHeaderIndex = std::string::npos;
    size_t currentHeaderIndex = std::string::npos;

    if (Acs.available() == 0)
    {
        // 如果串口没有数据等待接收，则通过串口发送指令，指令长度通过sizeof计算
        Acs.write(command, commandLength);
        acsStatus = false;
    }
    else
    {
        // 如果串口没有数据等待接收，则通过串口发送指令，指令长度通过sizeof计算
        Acs.write(command, commandLength);
        // 定义接收数据缓冲区的大小为37字节，根据传感器返回数据的长度确定
        int bufferSize = Acs.available();
        uint8_t buffer[bufferSize];
        acsBuffer.insert(acsBuffer.end(), buffer, buffer + Acs.readBytes(buffer, bufferSize));
        // Serial.print("数据：");
        // for (uint8_t dataByte : acsBuffer)
        // {
        //     Serial.print(dataByte, HEX);
        //     Serial.print(' ');
        // }
        // Serial.println();
    }
    if (acsBuffer.size() >= 37)
    {
        currentHeaderIndex = acsFindHeader(acsBuffer, syncHeader0, syncHeader1, syncHeader2, syncHeader3);

        if (currentHeaderIndex != std::string::npos)
        {
            prevHeaderIndex = acsFindHeader(acsBuffer, syncHeader0, syncHeader1, syncHeader2, syncHeader3, currentHeaderIndex + 3);
            if (prevHeaderIndex != std::string::npos)
            {
                // 提取两个数据头之间的数据
                std::vector<uint8_t> extractedData(acsBuffer.begin() + currentHeaderIndex, acsBuffer.begin() + prevHeaderIndex);
                if (CRC16(extractedData))
                {
                    SensorData(extractedData);
                }
                acsBuffer.erase(acsBuffer.begin(), acsBuffer.begin() + prevHeaderIndex);
            }
            else
            {
                if (CRC16(acsBuffer))
                {
                    SensorData(acsBuffer);
                    acsBuffer.clear();
                }
            }
        }
    }
}

// 串口初始化函数，用于设置Serial1串口的参数，使其能够正常进行数据通信
void setupAcs()
{
    // 初始化Serial1串口，设置波特率为serial1BaudRate（9600），数据位8位，无校验位，停止位1位，
    // 并指定发送引脚为serial1TxPin（10），接收引脚为serial1RxPin（11）
    Acs.begin(serial1BaudRate, SERIAL_8N1, serial1TxPin, serial1RxPin);
}

// 串口循环函数，用于周期性地执行与串口相关的操作，目前是调用读取传感器数据的函数
void loopAcs()
{
    static int f5 = 0;
    // 定义一个静态变量，用于记录上次读取数据的时间戳，实现定时读取数据的功能
    static long gridDataPreTick = 0;
    // 定义表示变量，用于区分指令
    static int commandFlag = 0;
    if (millis() - gridDataPreTick >= DataPreTick)
    { // 如果当前时间与上次读取数据的时间间隔大于等于100毫秒，执行以下操作
        if (commandFlag)
        {
            // 调用readSensorData函数，传入读取并网侧数据的指令，触发向传感器请求并网侧数据并进行后续处理
            readData(readGridCommand, sizeof(readGridCommand));
            commandFlag = 0;
        }
        else
        {
            // 调用readSensorData函数，传入读取用电侧数据的指令，触发向传感器请求用电侧数据并进行后续处理
            readData(readUsageCommand, sizeof(readUsageCommand));
            commandFlag = 1;
        }
        // Serial.println(f5);
        f5 = 0;
        // 更新上次读取数据的时间戳为当前时间，以便下次定时判断
        gridDataPreTick = millis();
    }
    f5++;
}
